package com.wang.tool.monitor.core;

import com.wang.tool.util.SimpleLogUtil;

import java.util.Arrays;
import java.util.function.Supplier;

/**
 * 链路记录器
 *
 * @Author wangshaopeng
 * @Date 2022/7/1
 */
public class LinkHolder {

    /**
     * 转换器
     */
    private static LinkTransmitter transmitter = new SimpleLinkTransmitter();

    /**
     * 获取当前link
     *
     * @return
     */
    public static LinkInfo getCurrent() {
        try {
            return transmitter == null ? null : transmitter.get();
        } catch (Throwable e) {
            SimpleLogUtil.error("transmitterGetError", e);
            return null;
        }
    }


    /**
     * 当前是否是debug状态
     *
     * @return
     */
    public static boolean isDebug() {
        LinkInfo linkInfo = LinkHolder.getCurrent();
        return linkInfo != null && linkInfo.isDebug();
    }


    /**
     * 主动设置debug状态
     * tips:此操作会影响子链路的情况
     *
     * @return 设置之前是否debug
     */
    public static Boolean markDebug(boolean debug) {
        LinkInfo linkInfo = LinkHolder.getCurrent();
        if (linkInfo == null) {
            return null;
        }
        boolean old = linkInfo.isDebug();
        linkInfo.markDebug(debug);
        return old;
    }

    /**
     * 标记当前节点有隐性异常
     */
    public static void markImplicitError(){
        LinkInfo linkInfo = LinkHolder.getCurrent();
        if (linkInfo == null) {
            return;
        }
        linkInfo.markImplicitError();
    }

    /**
     * 标记当前节点有隐性慢调用
     */
    public static void markImplicitSlow(){
        LinkInfo linkInfo = LinkHolder.getCurrent();
        if (linkInfo == null) {
            return;
        }
        linkInfo.markImplicitSlow();
    }



    /**
     * 添加信息
     * 信息会放于摘要载荷连同摘要日志一起输出
     * tips:简讯信息不需要和detail信息重复设置
     * @param key
     * @param val
     */
    public static void addDetail(String key, Object val) {
        if (key == null) {
            return;
        }
        LinkInfo linkInfo = getCurrent();
        if (linkInfo == null) {
            return;
        }
        linkInfo.getMonitor().addDetail(key, val);
    }

    /**
     * 添加debug载荷
     * 信息会放于摘要载荷连同摘要日志一起输出
     * 仅在debug场景有效
     * supplier可以减少无意义序列化
     * tips:简讯信息不需要和detail信息重复设置
     * @param key
     * @param supplier
     */
    public static void addDetailOnDebug(String key, Supplier<?> supplier) {
        if (key == null || supplier == null) {
            return;
        }
        LinkInfo linkInfo = getCurrent();
        if (linkInfo == null || !linkInfo.isDebug()) {
            return;
        }
        try {
            linkInfo.getMonitor().addDetail(key, supplier.get());
        } catch (Throwable e) {
            SimpleLogUtil.error("addDetailOnDebugError", e);
        }
    }



    /**
     * 添加信息
     * 信息会放于摘要载荷连同摘要日志一起输出
     * tips:简讯信息不需要和detail信息重复设置
     * @param key
     * @param val
     */
    public static void addShortDetail(String key, Object val) {
        if (key == null) {
            return;
        }
        LinkInfo linkInfo = getCurrent();
        if (linkInfo == null) {
            return;
        }
        linkInfo.addShortDetail(key, val);
    }

    /**
     * 添加debug载荷
     * 信息会放于摘要载荷连同摘要日志一起输出
     * 仅在debug场景有效
     * supplier可以减少无意义序列化
     * tips:简讯信息不需要和detail信息重复设置
     * @param key
     * @param supplier
     */
    public static void addShortDetailOnDebug(String key, Supplier<?> supplier) {
        if (key == null || supplier == null) {
            return;
        }
        LinkInfo linkInfo = getCurrent();
        if (linkInfo == null || !linkInfo.isDebug()) {
            return;
        }
        try {
            linkInfo.addShortDetail(key, supplier.get());
        } catch (Throwable e) {
            SimpleLogUtil.error("addShortDetailOnDebugError", e);
        }
    }

    /**
     * 添加提示信息
     * 某些场景下服务端想返回一些提示信息到结果中
     * 这时子节点可以分别记录自己的提示信息到自己的载荷中
     * 后面由某节点统一搜取并构建结果吐出去
     * supplier可以减少无意义序列化
     *
     * @param suppliers
     * @see MonitorInfo#buildTipsMap
     */
    public static void addTips(Supplier<String>... suppliers) {
        if (suppliers == null) {
            return;
        }
        LinkInfo linkInfo = getCurrent();
        if (linkInfo == null) {
            return;
        }
        try {
            String[] tips = Arrays.stream(suppliers).map(Supplier::get).toArray(String[]::new);
            linkInfo.getMonitor().addTips(tips);
        } catch (Throwable e) {
            SimpleLogUtil.error("addTipsError", e);
        }
    }

    /**
     * 添加提示信息,仅在debug场景有效
     * 某些场景下服务端想返回一些提示信息到结果中
     * 这时子节点可以分别记录自己的提示信息到自己的载荷中
     * 后面由某节点统一搜取并构建结果吐出去
     * supplier可以减少无意义序列化
     *
     * @param suppliers
     * @see MonitorInfo#buildTipsMap
     */
    public static void addTipsOnDebug(Supplier<String>... suppliers) {
        if (suppliers == null) {
            return;
        }
        LinkInfo linkInfo = getCurrent();
        if (linkInfo == null || !linkInfo.isDebug()) {
            return;
        }
        try {
            String[] tips = Arrays.stream(suppliers).map(Supplier::get).toArray(String[]::new);
            linkInfo.getMonitor().addTips(tips);
        } catch (Throwable e) {
            SimpleLogUtil.error("addTipsOnDebugError", e);
        }
    }


    /**
     * 预设当前节点的状态码
     * @param status
     * @return
     */
    public static boolean preSetStatus(String status) {
        if (status == null) {
            return false;
        }
        LinkInfo linkInfo = LinkHolder.getCurrent();
        if (linkInfo == null) {
            return false;
        }
        linkInfo.setStatus(status);
        return true;
    }


    /*-------------------------分割线-----------------------*/


    /**
     * 设置当前线程对象
     */
    public static void setCurrent(LinkInfo linkInfo) {
        if (transmitter != null) {
            try {
                transmitter.set(linkInfo);
            } catch (Throwable e) {
                SimpleLogUtil.error("transmitterSetError", e);
            }
        }
    }

    /**
     * 删除当前线程对象
     */
    public static void clear() {
        setCurrent(null);
    }


    public static LinkTransmitter getTransmitter() {
        return transmitter;
    }

    /**
     * 设置链路传送器
     *
     * @param transmitter
     */
    public static void setTransmitter(LinkTransmitter transmitter) {
        LinkHolder.transmitter = transmitter;
        SimpleLogUtil.info("[changeLinkTransmitter] transmitter:{0}", transmitter);
    }
}
